import datetime
import re
from os import stat, makedirs, rename, umask
from os.path import expanduser
from subprocess import Popen
import subprocess
import socket
import os
import glob
import random
import dochallenge


testing = socket.gethostname() != 'pc.cs.purdue.edu'

if testing:
    TRACES_DIR = 'applications/icypc/private/ttraces'
else:
    TRACES_DIR = 'applications/icypc/private/ttraces'
    ICYPC_JAR = '/homes/cs390cp/icypc/icypc.jar'
    MAPS_DIR = '/homes/cs390cp/icypc/maps'

def chooseMap():
    files = glob.glob(os.path.join(MAPS_DIR, '*.txt'))
    return random.choice(files)

def makeTraceDir():
    try:
        mode = stat(TRACES_DIR).st_mode
        if (mode & 0040000 == 0):  # S_IFDIR = 0040000
            print('%s is not a directory or not accessible -- tell a friend' % TRACES_DIR)
    except OSError:
        try:
            makedirs(TRACES_DIR)
        except:
            print('could not create %s == tell a friend' % TRACES_DIR)


output_pattern = re.compile(r'.*(Winner: (\d+)|Tie)\sScore: (\d+) \((\d+) (\d+)\) (\d+) \((\d+) (\d+)\)', re.DOTALL)

def runMatch(id, red, blue):
    # Identify the contenders with several convenience variables...
    (redUser, redPlayer) = red
    (blueUser, bluePlayer) = blue
    
    now = datetime.datetime.now()
    
    print("\tMATCH %s at %s" % (id, now))
    
    # Format the command string and start and wait for the subprocesses...
    if testing:
        runtemplate = 'applications/icypc/private/domatch %s %s %s/%s-trace.txt'
        command = (runtemplate % (dochallenge.script(redUser, redPlayer), dochallenge.script(blueUser, bluePlayer), TRACES_DIR, id)).split()
    else:
        runtemplate = 'java -jar %s -player pipe 1 %s -player pipe 1 %s -view trace %s/%s-trace.txt'
        map = chooseMap()
        runtemplate += ' -map %s' % map
        print('\tusing map %s' % map)
        command = (runtemplate % (ICYPC_JAR, dochallenge.script(redUser, redPlayer), dochallenge.script(blueUser, bluePlayer), TRACES_DIR, id)).split()
    p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (output, stderr) = p.communicate()
        
    # Parse the returned output, looking for winners, losers, and tie score
    m = output_pattern.match(output)
    if m:  # parsing OK, we have a winner and loser (or tie)
        red_score = '[%s (%s %s)]' % (m.group(3), m.group(4), m.group(5))
        blue_score = '[%s (%s %s)]' % (m.group(6), m.group(7), m.group(8))

        if m.group(2) == '0':  # red (challenger) wins
            winnerColor = 'red'
            (winner, loser) = ((redUser, redPlayer), (blueUser, bluePlayer))
            (winner_score, loser_score) = (m.group(3), m.group(6))
        elif m.group(2) == '1':  # blue (defender) wins
            winnerColor = 'blue'
            (winner, loser) = ((blueUser, bluePlayer), (redUser, redPlayer))
            (winner_score, loser_score) = (m.group(6), m.group(3))
        else:  # Tie
            winnerColor = "Tie"
            
        # Write stdout, stderr, and info to trace directory files
        try:
            f = open('%s/%s-stdout.txt' % (TRACES_DIR, id), 'w'); f.write(output); f.close()
            f = open('%s/%s-stderr.txt' % (TRACES_DIR, id), 'w'); f.write(stderr); f.close()
            info = "%s/%s (red) vs. %s/%s (blue)\n" % (redUser, redPlayer, blueUser, bluePlayer)
            f = open('%s/%s-info.txt' % (TRACES_DIR, id), 'w'); f.write(info); f.close()
        except:
            logger.critical('could not write stdout/err files in %s--tell a friend' % TRACES_DIR)
    else: # parsing failed, bench the challenger (just in case), delete the match, but log it
        logger.info('\tmatch output parse failure; benching challenger.')
        logger.error('no winner found in stdout; probably a script was not executable; stdout and stderr follow')
        logger.error('\n===== begin stdout =====\n%s\n===== end stdout =====' % output)
        logger.error('\n===== begin stderr =====\n%s\n===== end stderr =====' % stderr)

    delta = int(winner_score) - int(loser_score)
    print("\t\t%s wins by %d" % (winnerColor, delta))
    return (winnerColor, delta)

    
TOURNAMENT_SIZE = 8
GAMES_PER_MATCH = 5

def runBestOfSeries(round, match, red, blue):
    red_wins = 0; red_min = 10000
    blue_wins = 0; blue_min = 10000
    for i in range(GAMES_PER_MATCH):
        id = str(round) + str(match) + str(i)
        (winnerColor, delta) = runMatch(id, red, blue)
        if winnerColor == 'red':
            red_wins += 1
            if delta < red_min:
                red_min = delta
                red_closest = id
        elif winnerColor == 'blue':
            blue_wins += 1
            if delta < blue_min:
                blue_min = delta
                blue_closest = id
        else:
            blue_wins += 1  # tie goes to the underdog

    if red_wins > blue_wins:  # tie goes to the underdog
        (user, player) = red
        closest = red_closest
        delta = red_min
    else:
        (user, player) = blue
        closest = blue_closest
        delta = blue_min
    return (user, player, closest, delta)

def runtournament():
    ladderadjust = local_import('ladderadjust', reload=True)
    ladder = ladderadjust.buildLadder(db)

    i = 0
    competitors = []
    users = []

    for competitor in ladder:
        user = competitor[0]
        player = competitor[1]
        
        if user in users:
            print("ignoring (%s,%s)" % (user, player))
        else:
            i += 1
            users.append(user)
            competitors.append((user, player))
            print("%d: %s/%s" % (i, user, player))
            if i == TOURNAMENT_SIZE:
                break
    
    makeTraceDir()
    results = open('%s/results' % TRACES_DIR, 'w')
    results.write("#!/bin/bash\n")

    round = 1
    while (len(competitors) > 1):
        print("===== ROUND %d =====" % round)
        next = []
        for series in range(len(competitors)/2):
            red = competitors[series]
            blue = competitors[len(competitors)-series-1]
            print("    BEGIN SERIES %d: %s/%s vs %s/%s" % (series, red[0], red[1], blue[0], blue[1]))
            (user, player, closest, delta) = runBestOfSeries(round, series, red, blue)
            print("    END SERIES %d: %s/%s wins; closest match %s" % (series, user, player, closest))
            results.write("echo playing match %s, winner %s/%s by %d\n" % (closest, user, player, delta))
            results.write("v3d %s\n" % closest)
            next.append((user, player))
        competitors = next
        round += 1
    results.close()

runtournament()
